using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using UnityEditor;
using UnityEngine;

namespace AudioStudio
{
    public class SearchWindow : EditorWindow
    {
        private delegate bool ComponentParser(GameObject go, string fullName, string prefab);
        private delegate bool ComponentImporter(GameObject go, Component component, XElement node, string prefab);
        private delegate void PrefabParser(string prefab);
        private Dictionary<string, bool> _components = new Dictionary<string, bool>();
        private Dictionary<string, ComponentParser> _searchers = new Dictionary<string, ComponentParser>();
        private Dictionary<string, ComponentParser> _refresher = new Dictionary<string, ComponentParser>();
        private Dictionary<string, ComponentParser> _exporters = new Dictionary<string, ComponentParser>();
        private Dictionary<string, ComponentImporter> _importers = new Dictionary<string, ComponentImporter>();
        private Dictionary<string, Type> _types = new Dictionary<string, Type>();
        private bool IsDeepSearch = true;
        private bool IsRefreshAndUpgrade = true;
        private XElement XmlRoot = new XElement("Root");

        public SearchWindow()
        {
            RegistComponent<AnimationSound>(AnimationSoundRefresher,null,AnimationSoundExporter, AnimationSoundImporter);
            RegistComponent<ButtonSound>(ButtonSoundRefresher, null, null, ButtonSoundImporter);
            RegistComponent<ToggleSound>(ToggleSoundRefresher, null, null, ToggleSoundImporter);            
            RegistComponent<EffectSound>(EffectSoundRefresher,null,null, EffectSoundImporter);
            RegistComponent<MenuSound>(MenuSoundRefresher, null, null, MenuSoundImporter);
            RegistComponent<SetState>(SetStateRefresher, null, null, SetStateImporter);
            RegistComponent<SliderSound>(SliderSoundRefresher, null, null, SliderSoundImporter);
            RegistComponent<DropdownSound>(DropdownSoundRefresher, null, null, DropdownSoundImporter);
            RegistComponent<SoundBanks>(null);
            RegistComponent<DefaultAudioListener>();
            RegistComponent<AudioInit>();
        }

        [MenuItem("AudioStudio/Search")]
        public static void ShowWindow()
        {
            EditorWindow.GetWindow(typeof(SearchWindow));
        }

        public static void ExportComponentsToFile(string[] names,string filename)
        {
            SearchWindow sw = EditorWindow.GetWindow(typeof(SearchWindow)) as SearchWindow;
            if(sw != null)
            {
                sw.SelectAllToggle(false);
                foreach(string s in names)
                {
                    string ss = s;
                    if(!s.StartsWith("AudioStudio."))
                    {
                        ss = "AudioStudio." + s;
                    }
                    sw.SelectToggle(ss);
                }
                sw.ExportToFile(filename);
            }
        }

        public static void ImportComponentsFromFile(string filename)
        {
            SearchWindow sw = EditorWindow.GetWindow(typeof(SearchWindow)) as SearchWindow;
            if(sw != null)
            {
                sw.ImportFromFile("AutoImport",filename);
            }
        }

        private void SelectToggle(string name)
        {
            if(_components.ContainsKey(name))
            {
                _components[name] = true;
            }
        }

        private bool Checker<T>(GameObject go, string fullName, string prefab)
        {
            var c = go.GetComponent<T>();
            if (c != null)
            {
                return true;
            }
            return false;
        }

        private bool refresher<T>(GameObject go, string fullName, string prefab)
        {
            var c = go.GetComponent<T>();
            if (c != null)
            {
                return true;
            }
            return false;
        }

        private bool Remover<T>(GameObject go, string fullName, string prefab)
        {
            UnityEngine.Object c = go.GetComponent<T>() as UnityEngine.Object;
            if (c != null)
            {
                try
                {
                    DestroyImmediate(c,true);
                    AkSoundEngine.Log(string.Format("Remove Component:{0} in:{1} at:{2}", typeof(T).ToString(), prefab, fullName));
                }
                catch(Exception e)
                {
                    AkSoundEngine.Log(string.Format("Coundn't Remove Component:{0} in:{1} at:{2} because:{3}", typeof(T).ToString(), prefab, fullName,e.ToString()));
                }
                return true;
            }
            return false;
        }

        private void RegistComponent<T>(ComponentParser refresher = null, ComponentParser searcher = null, ComponentParser exporter = null, ComponentImporter importer = null)
        {
            string s = typeof(T).ToString();
            _components[s] = true;
            _types[s] = typeof(T);
            _searchers[s] = (searcher == null) ? Checker<T> : searcher;
            _refresher[s] = (refresher == null) ? Checker<T> : refresher;
            _exporters[s] = (exporter == null) ? ComponentExporter<T> : exporter;
            _importers[s] = importer;
        }

        private void OnGUI()
        {
            using (new EditorGUILayout.VerticalScope(GUI.skin.box))
            {
                using (new EditorGUILayout.HorizontalScope(GUI.skin.box))
                {
                    GUILayout.BeginHorizontal();
                    GUILayout.Label("Select Components");
                    if (GUILayout.Button("Select All", GUILayout.Width(120)))
                    {
                        SelectAllToggle(true);
                    }
                    if (GUILayout.Button("Cancel Select All", GUILayout.Width(120)))
                    {
                        SelectAllToggle(false);
                    }
                    GUILayout.EndHorizontal();
                }

                using (new EditorGUILayout.VerticalScope(GUI.skin.box))
                {
                    ShowToggles();
                }

                using (new EditorGUILayout.HorizontalScope(GUI.skin.box))
                {
                    IsDeepSearch = GUILayout.Toggle(IsDeepSearch, "Search All Child GameObject");
                    IsRefreshAndUpgrade = GUILayout.Toggle(IsRefreshAndUpgrade, "Refresh & Upgrade");
                    if (GUILayout.Button("Search"))
                    {
                        EditorApplication.delayCall += Search;
                    }
                }
                using (new EditorGUILayout.VerticalScope(GUI.skin.box))
                {
                    if (GUILayout.Button("Export"))
                    {
                        EditorApplication.delayCall += Export;
                    }
                    if (GUILayout.Button("Import"))
                    {
                        EditorApplication.delayCall += Import;
                    }
                }
            }
        }

        private void Search()
        {
            ParsePrefabs(ParsePrefab, "Search");
        }

        private void ShowToggles()
        {
            Dictionary<string, bool> selects = new Dictionary<string, bool>(_components);
            foreach (var i in selects)
            {
                bool isSelected = GUILayout.Toggle(i.Value, i.Key);
                if (isSelected != i.Value)
                {
                    _components[i.Key] = isSelected;
                }
            }
        }

        private void SelectAllToggle(bool select)
        {
            Dictionary<string, bool> selects = new Dictionary<string, bool>(_components);
            foreach (var i in selects)
            {
                _components[i.Key] = select;
            }
        }

        private void ParsePrefabs(PrefabParser parser,string title)
        {
            string[] files = Directory.GetFiles(Application.dataPath, "*.prefab", SearchOption.AllDirectories);
            EditorUtility.DisplayCancelableProgressBar(title, "wait...", 0);
            for (var i = 0; i < files.Length; i++)
            {
                string shortpath = files[i].Replace(@"\", "/");
                shortpath = shortpath.Substring(shortpath.IndexOf("Assets"));
                if (EditorUtility.DisplayCancelableProgressBar(title, shortpath, (i + 1) * 1.0f / files.Length))
                {
                    break;
                }
                parser(shortpath);
            }
            EditorUtility.ClearProgressBar();
        }

        private void ParsePrefab(string shortpath)
        {
            try
            {
                GameObject go = (GameObject)AssetDatabase.LoadAssetAtPath(shortpath, typeof(GameObject));
                if (go != null)
                {
                    if (IsRefreshAndUpgrade)
                    {
                        if (ParseGameObject(go, string.Empty, shortpath, false, false, false))
                        {
                            GameObject instance = PrefabUtility.InstantiatePrefab(go) as GameObject;
                            if (ParseGameObject(instance, string.Empty, shortpath, true, true, false))
                            {
                                PrefabUtility.ReplacePrefab(instance, go, ReplacePrefabOptions.ConnectToPrefab);
                            }
                            UnityEngine.Object.DestroyImmediate(instance);
                        }
                    }
                    else
                    {
                        ParseGameObject(go, string.Empty, shortpath, false, true, true);
                    }
                }
            }
            catch (Exception e)
            {
                AkSoundEngine.Log(string.Format("Parse prefab '{0}' errir:{1}", shortpath, e.ToString()));
            }
        }

        private bool ParseGameObject(GameObject go, string fullName, string prefab, bool refresh, bool findAll, bool log)
        {
            string newFullName = (fullName.Length > 0) ? (fullName + "." + go.name) : go.name;
            bool found = false;
            foreach (var i in _components)
            {
                if (i.Value)
                {
                    if (refresh)
                    {
                        if (_searchers[i.Key](go, newFullName, prefab))
                        {
                            if (_refresher[i.Key](go, newFullName, prefab))
                            {
                                found = true;
                                if (!findAll)
                                {
                                    return found;
                                }
                            }
                        }
                    }
                    else
                    {
                        if (_searchers[i.Key](go, newFullName, prefab))
                        {
                            found = true;
                            if (log)
                            {
                                AkSoundEngine.Log(string.Format("Find Component:{0} in:{1} at:{2}", i.Key, prefab, newFullName));
                            }
                            if (!findAll)
                            {
                                return found;
                            }
                        }
                    }
                }
            }
            if (IsDeepSearch)
            {
                foreach (Transform child in go.transform)
                {
                    if (ParseGameObject(child.gameObject, newFullName, prefab, refresh, findAll, log))
                    {
                        found = true;
                        if (!findAll)
                        {
                            return found;
                        }
                    }
                }
            }
            return found;
        }

        private static bool GuidEqual(byte[] a, byte[] b)
        {
            if (a.Length == b.Length)
            {
                for (var i = 0; i < a.Length; i++)
                {
                    if (a[i] != b[i])
                        return false;
                }
                return true;
            }
            return false;
        }

        private static bool CopyGuid(byte[] from, byte[] to)
        {
            if (from.Length == to.Length)
            {
                from.CopyTo(to, 0);
                return true;
            }
            return false;
        }

        private static void ClearGuid(byte[] bytes)
        {
            for (var i = 0; i < bytes.Length; i++)
            {
                bytes[i] = 0;
            }
        }
        #region Refresh
        public static bool ParseEvent(ref AudioEvent audioEvent, string fullName, string prefab, string typeName)
        {
            if (audioEvent.name.Equals(string.Empty))
            {
                AkSoundEngine.LogError(string.Format("Event not set in:{0}->{1} of:{2}", prefab, fullName, typeName));
                return false;
            }
            string eventName = audioEvent.name;
            var e = AkWwiseProjectData.Instance.FindEvent(eventName);
            if (e != null)
            {
                CopyGuid(e.Guid , audioEvent.valueGuid);
                if (e.ID != audioEvent.ID)
                {
                    AkSoundEngine.LogWarning(string.Format("Refresh Event ID in:{0}->{1} of:{2} [{3}->{4}]", prefab, fullName, typeName, audioEvent.ID, e.ID));
                    audioEvent.ID = e.ID;
                    return true;
                }
                return false;
            }
            else
            {
                AkSoundEngine.LogWarning(string.Format("Clear Event ID&Name in:{0}->{1} of:{2} oldname:{3}", prefab, fullName, typeName, eventName));
                audioEvent.name = string.Empty;
                audioEvent.ID = 0;
                ClearGuid(audioEvent.valueGuid);
            }

            return true;
        }

        public static bool ParseRTPC(ref RTPC rtpc, string fullName, string prefab, string typeName)
        {
            if (rtpc.name.Equals(string.Empty))
            {
                AkSoundEngine.LogError(string.Format("Event not set in:{0}->{1} of:{2}", prefab, fullName, typeName));
                return false;
            }

            var list = AkWwiseProjectData.Instance.RtpcWwu;
            string name = rtpc.name;
            bool bchanged = false;

            for (int i = 0; i < list.Count; i++)
            {
                var vrt = list[i].List.Find(x => x.Name.Equals(name));

                if (vrt != null)
                {
                    if (rtpc.ID != vrt.ID)
                    {

                        rtpc.ID = vrt.ID;
                        AkSoundEngine.LogWarning(string.Format("Refresh rtpc ID in:{0}->{1} of:{2}", prefab, fullName, typeName));
                        bchanged = true;
                    }
                    if (!GuidEqual(rtpc.valueGuid, vrt.Guid))
                    {
                        AkSoundEngine.LogWarning(string.Format("Refresh rtpc Guid in:{0}->{1} of:{2}", prefab, fullName, typeName));
                        CopyGuid(vrt.Guid, rtpc.valueGuid);
                        bchanged = true;
                    }
                }
            }
            return bchanged;
        }

        public static bool ParseEventPC(ref AudioEventPC audioEvent, string fullName, string prefab, string typeName)
        {
            if (audioEvent.name.Equals(string.Empty))
            {
                AkSoundEngine.LogError(string.Format("Event not set in:{0}->{1} of:{2}", prefab, fullName, typeName));
                return false;
            }
            string eventName = audioEvent.name;
            string eventNamePC = eventName + AudioManager.PlayerCtrlSuffix;

            var epc = AkWwiseProjectData.Instance.FindEvent(eventNamePC);
            if (epc != null)
            {
                audioEvent.ID_PC = (uint)epc.ID;
                AkSoundEngine.LogWarning(string.Format("Refresh Event ID_PC in:{0}->{1} of:{2} [{3}->{4}]", prefab, fullName, typeName, audioEvent.ID_PC, epc.ID));
            }
            else
            {
                AkSoundEngine.LogWarning(string.Format("Clear Event ID_PC in:{0}->{1} of:{2} oldname:{3}", prefab, fullName, typeName, eventName));
                audioEvent.ID_PC = 0;
            }

            var e = AkWwiseProjectData.Instance.FindEvent(eventName);
            if (e != null)
            {
                CopyGuid(e.Guid, audioEvent.valueGuid);
                if (e.ID != audioEvent.ID)
                {
                    AkSoundEngine.LogWarning(string.Format("Refresh Event ID in:{0}->{1} of:{2} [{3}->{4}]", prefab, fullName, typeName, audioEvent.ID, e.ID));
                    audioEvent.ID = e.ID;
                    return true;
                }
                return false;
            }
            else
            {
                AkSoundEngine.LogWarning(string.Format("Clear Event ID&Name in:{0}->{1} of:{2} oldname:{3}", prefab, fullName, typeName, eventName));
                audioEvent.name = string.Empty;
                audioEvent.ID = 0;
                ClearGuid(audioEvent.valueGuid);
            }

            return true;
        }

        public static bool ParseState(ref State state, string fullName, string prefab, string typeName)
        {
            if (state.name.Equals(string.Empty) || state.groupName.Equals(string.Empty))
            {
                AkSoundEngine.LogError(string.Format("State not set in:{0}->{1} of:{2}", prefab, fullName, typeName));
                return false;
            }
            var list = AkWwiseProjectData.Instance.StateWwu;
            string groupName = state.groupName;
            string name = state.name;
            bool bchanged = false;

            for (int i = 0; i < list.Count; i++)
            {
                var group = list[i].List.Find(x => x.Name.Equals(groupName));

                if (group != null)
                {
                    if (state.groupID != group.ID)
                    {
                        AkSoundEngine.LogWarning(string.Format("Refresh State GroupID in:{0}->{1} of:{2}", prefab, fullName, typeName));
                        state.groupID = group.ID;
                        bchanged = true;
                    }

                    if (!GuidEqual(state.groupGuid, group.Guid))
                    {
                        AkSoundEngine.LogWarning(string.Format("Refresh State GroupGuid in:{0}->{1} of:{2}", prefab, fullName, typeName));
                        CopyGuid(group.Guid, state.groupGuid);
                        bchanged = true;
                    }

                    int index = group.values.FindIndex(x => x.Equals(name));

                    if (index < 0)
                    {
                        state.name = string.Empty;
                        state.ID = 0;
                        ClearGuid(state.valueGuid);
                        state.groupName = string.Empty;
                        state.groupID = 0;
                        ClearGuid(state.groupGuid);
                        AkSoundEngine.LogWarning(string.Format("Clear State ID&Name in:{0}->{1} of:{2}", prefab, fullName, typeName));
                        bchanged = true;
                    }

                    if (state.ID != group.valueIDs[index])
                    {

                        state.ID = group.valueIDs[index];
                        AkSoundEngine.LogWarning(string.Format("Refresh State ID in:{0}->{1} of:{2}", prefab, fullName, typeName));
                        bchanged = true;
                    }
                    if (!GuidEqual(state.valueGuid, group.ValueGuids[index].bytes))
                    {
                        CopyGuid(group.ValueGuids[index].bytes , state.valueGuid);
                        AkSoundEngine.LogWarning(string.Format("Refresh State Guid in:{0}->{1} of:{2}", prefab, fullName, typeName));
                        bchanged = true;
                    }
                }
            }

//             state.name = string.Empty;
//             state.ID = 0;
//             ClearGuid(state.valueGuid);
//             state.groupName = string.Empty;
//             state.groupID = 0;
//             ClearGuid(state.groupGuid);
//             AkSoundEngine.LogWarning(string.Format("Clear State ID&Name in:{0}->{1} of:{2}", prefab, fullName, typeName));
            return bchanged;
        }

        private bool AnimationSoundRefresher(GameObject go, string fullName, string prefab)
        {
            var old = go.GetComponent<AnimationSound>();
            if (old != null)
            {
                Animator animator = go.GetComponent<Animator>();
                if (animator != null)
                {
                    bool replaced = false;
                    foreach (AnimationClip clip in animator.runtimeAnimatorController.animationClips)
                    {
                        replaced = AnimationClipModify.UpdateFunctionName(clip);
                    }
                    return replaced;
                }
            }
            return false;
        }

        private bool ButtonSoundRefresher(GameObject go, string fullName, string prefab)
        {
            var old = go.GetComponent<ButtonSound>();
            if (old != null)
            {
                bool changed = false;
                if (old.audioEvents != null)
                {
                    for(var i = 0; i < old.audioEvents.Length; i++)
                    {
                        changed |= ParseEvent(ref old.audioEvents[i], fullName, prefab, "ButtonSound");
                    }
                    return changed;
                }
                if (old.mouseinEvent != null)
                {
                    changed |= ParseEvent(ref old.mouseinEvent, fullName, prefab, "ButtonSound");
                }
            }
            return false;
        }

        private bool ToggleSoundRefresher(GameObject go, string fullName, string prefab)
        {
            var old = go.GetComponent<ToggleSound>();
            if (old != null)
            {
                bool changed = false;
                if (old.toggleaudioEvents != null)
                {
                    for (var i = 0; i < old.toggleaudioEvents.Length; i++)
                    {
                        changed |= ParseEvent(ref old.toggleaudioEvents[i], fullName, prefab, "ToggleSound");
                    }
                }
                if (old.untoggleaudioEvents != null)
                {
                    for (var i = 0; i < old.untoggleaudioEvents.Length; i++)
                    {
                        changed |= ParseEvent(ref old.untoggleaudioEvents[i], fullName, prefab, "ToggleSound");
                    }
                }
                return changed;
            }
            return false;
        }

        private bool EffectSoundRefresher(GameObject go, string fullName, string prefab)
        {
            var old = go.GetComponent<EffectSound>();
            if (old != null)
            {
                bool changed = false;
                if (old.enables != null)
                {
                    for (var i = 0; i < old.enables.Length; i++)
                    {
                        changed |= ParseEventPC(ref old.enables[i].audioEvent, fullName, prefab, "EffectSound");
                    }
                }
                if (old.disables != null)
                {
                    for (var i = 0; i < old.disables.Length; i++)
                    {
                        changed |= ParseEventPC(ref old.disables[i], fullName, prefab, "EffectSound");
                    }
                }
                return changed;
            }
            return false;
        }

        private bool MenuSoundRefresher(GameObject go, string fullName, string prefab)
        {
            var old = go.GetComponent<MenuSound>();
            if (old != null)
            {
                bool changed = false;
                if (old.Opens != null)
                {
                    for (var i = 0; i < old.Opens.Length; i++)
                    {
                        changed |= ParseEvent(ref old.Opens[i], fullName, prefab, "MenuSound");
                    }
                }
                if (old.Closes != null)
                {
                    for (var i = 0; i < old.Closes.Length; i++)
                    {
                        changed |= ParseEvent(ref old.Closes[i], fullName, prefab, "MenuSound");
                    }
                }
                return changed;
            }
            return false;
        }

        private bool SetStateRefresher(GameObject go, string fullName, string prefab)
        {
            var old = go.GetComponent<SetState>();
            if (old != null)
            {
                bool refreshed = false;
                for (var i = 0; i < old.States.Length; i++)
                {
                    refreshed |= ParseState(ref old.States[i].state, fullName, prefab, "SetState");
                }
                return refreshed;
            }
            return false;
        }

        private bool SliderSoundRefresher(GameObject go, string fullName, string prefab)
        {
            var old = go.GetComponent<SliderSound>();
            if (old != null)
            {
                bool refreshed = false;
                refreshed = ParseRTPC(ref old.ValueChangeRTPC, fullName, prefab, "SliderSound");
                return refreshed;
            }
            return false;
        }

        private bool DropdownSoundRefresher(GameObject go, string fullName, string prefab)
        {
            var old = go.GetComponent<DropdownSound>();
            if (old != null)
            {
                bool changed = false;
                if (old.ValueChangeEvents != null)
                {
                    for (var i = 0; i < old.ValueChangeEvents.Length; i++)
                    {
                        changed |= ParseEvent(ref old.ValueChangeEvents[i], fullName, prefab, "DropdownSound");
                    }
                }
                if (old.PopupEvents != null)
                {
                    for (var i = 0; i < old.PopupEvents.Length; i++)
                    {
                        changed |= ParseEvent(ref old.PopupEvents[i], fullName, prefab, "DropdownSound");
                    }
                }
                if (old.CloseEvents != null)
                {
                    for (var i = 0; i < old.CloseEvents.Length; i++)
                    {
                        changed |= ParseEvent(ref old.CloseEvents[i], fullName, prefab, "DropdownSound");
                    }
                }
                return changed;
            }
            return false;
        }
        #endregion

        #region export
        private void Export()
        {
            string path = EditorUtility.SaveFilePanel("export", Application.dataPath, "AudioStudio.xml", ".xml");
            ExportToFile(path);
        }

        private void ExportToFile(string filename)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = new UTF8Encoding(false);
            settings.Indent = true;
            XmlWriter xw = XmlWriter.Create(filename, settings);
            ParsePrefabs(ExportPrefab,"Export");
            XmlRoot.Save(xw);
            XmlRoot.RemoveAll();
            //д���ļ�
            xw.Flush();
            xw.Close();
        }

        private void ExportPrefab(string prefab)
        {
            GameObject go = (GameObject)AssetDatabase.LoadAssetAtPath(prefab, typeof(GameObject));
            if (go != null)
            {
                ExportGameObject(go, string.Empty, prefab);
            }
        }

        private bool ExportGameObject(GameObject go, string fullName, string prefab)
        {
            string newFullName = (fullName.Length > 0) ? (fullName + "." + go.name) : go.name;
            bool found = false;
            foreach (var i in _components)
            {
                if (i.Value)
                {
                    if (_exporters[i.Key](go, newFullName, prefab))
                    {
                        found = true;
                    }
                }
            }
            foreach (Transform child in go.transform)
            {
                if (ExportGameObject(child.gameObject, newFullName,prefab))
                {
                    found = true;
                }
            }
            return found;
        }


        private bool AnimationSoundExporter(GameObject go, string fullName, string prefab)
        {
            var s = go.GetComponent<AnimationSound>();
            if (s != null)
            {
                var xml = new XElement("component");
                xml.SetAttributeValue("type", typeof(AnimationSound).ToString());
                xml.Add(new XElement("path", Path.GetDirectoryName(prefab)));
                xml.Add(new XElement("prefab", Path.GetFileName(prefab)));
                xml.Add(new XElement("GameObject", fullName));
                xml.Add(new XElement("IsUpdatePosition", s.IsUpdatePosition));

                Animator animator = go.GetComponent<Animator>();
                if (animator == null)
                {
                    return false;
                }
                foreach (AnimationClip clip in animator.runtimeAnimatorController.animationClips)
                {
                    var xmlClip = new XElement("clip");
                    foreach (UnityEngine.AnimationEvent evt in clip.events)
                    {
                        if(AudioUtility.IsSoundAnimationEvent(evt))
                        {
                            var ae = new XElement("AnimationEvent");
                            ae.Add(new XElement("functionName", evt.functionName));
                            ae.Add(new XElement("paramString", evt.stringParameter));
                            ae.Add(new XElement("time", (int)evt.time * 1000));
                            xmlClip.Add(ae);

                        }
                    }
                    if(!xmlClip.IsEmpty)
                    {
                        xmlClip.Add(new XElement("name", clip.name));
                        xml.Add(xmlClip);
                    }
                }
                XmlRoot.Add(xml);

                return true;
            }
            return false;
        }
        
        private void SerializeToXml(System.Object obj,Type type, XElement xml)
        {
            var fields = type.GetFields();
            foreach (var f in fields)
            {
                if (f.IsPublic)
                {
                    try
                    {
                        Type fieldType = f.FieldType;
                        if (fieldType == typeof(AudioEvent))
                        {
                            xml.Add(new XElement(f.Name, (f.GetValue(obj) as AudioEvent).name));
                        }
                        else if (fieldType == typeof(AudioEventPC))
                        {
                            xml.Add(new XElement(f.Name, (f.GetValue(obj) as AudioEventPC).name));
                        }
                        else if (fieldType == typeof(RTPC))
                        {
                            xml.Add(new XElement(f.Name, (f.GetValue(obj) as RTPC).name));
                        }
                        else if (fieldType == typeof(Trigger))
                        {
                            xml.Add(new XElement(f.Name, (f.GetValue(obj) as Trigger).name));
                        }
                        else if (fieldType == typeof(State))
                        {
                            var s = f.GetValue(obj) as State;
                            xml.Add(new XElement(f.Name, s.groupName + '.' + s.name));
                        }
                        else if (fieldType == typeof(Switch))
                        {
                            var s = f.GetValue(obj) as Switch;
                            xml.Add(new XElement(f.Name, s.groupName + '.' + s.name));
                        }
                        else if (fieldType == typeof(AuxBus))
                        {
                            xml.Add(new XElement(f.Name, (f.GetValue(obj) as AuxBus).name));
                        }
                        else if (fieldType == typeof(Bank))
                        {
                            xml.Add(new XElement(f.Name, (f.GetValue(obj) as Bank).name));
                        }
                        else if(fieldType.IsArray)
                        {
                            var elemType = fieldType.GetElementType();

                            var arrayXml = new XElement(f.Name);
                            var array = f.GetValue(obj) as Array;
                            for (var i = 0; i < array.Length; i++)
                            {
                                XElement idx;
                                if (elemType == typeof(AudioEvent))
                                {
                                    idx = new XElement(elemType.ToString(), (array.GetValue(i) as AudioEvent).name);
                                }
                                else if (elemType == typeof(AudioEventPC))
                                {
                                    idx = new XElement(elemType.ToString(), (array.GetValue(i) as AudioEventPC).name);
                                }
                                else if (elemType == typeof(RTPC))
                                {
                                    idx = new XElement(elemType.ToString(), (array.GetValue(i) as RTPC).name);
                                }
                                else if (elemType == typeof(Trigger))
                                {
                                    idx = new XElement(elemType.ToString(), (array.GetValue(i) as Trigger).name);
                                }
                                else if (elemType == typeof(State))
                                {
                                    var s = f.GetValue(obj) as State;
                                    idx = new XElement(elemType.ToString(), s.groupName + '.' + s.name);
                                }
                                else if (elemType == typeof(Bank))
                                {
                                    idx = new XElement(elemType.ToString(), (array.GetValue(i) as Bank).name);
                                }
                                else
                                {
                                    idx = new XElement(elemType.ToString());
                                    SerializeToXml(array.GetValue(i), elemType, idx);
                                }
                                arrayXml.Add(idx);
                            }
                            xml.Add(arrayXml);
                        }
                        else if(fieldType.IsClass)
                        {
                            if (fieldType == typeof(System.String))
                            {
                                xml.Add(new XElement(f.Name ,(f.GetValue(obj) as String)));
                            }
                            else
                            {
                                var child = new XElement(fieldType.ToString());
                                SerializeToXml(f.GetValue(obj), fieldType, child);
                                xml.Add(child);
                            }
                        }
                        else
                        {
                            xml.Add(new XElement(f.Name, f.GetValue(obj)));
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.LogException(e);
                    }
                }
            }
        }

        private bool ComponentExporter<T>(GameObject go, string fullName, string prefab)
        {
            bool changed = false;
            foreach(var s in go.GetComponents<T>())
            {
                var xml = new XElement("component");
                xml.SetAttributeValue("type", typeof(T).ToString());
                var path = Path.GetDirectoryName(prefab);
                xml.Add(new XElement("path", path));
                var fileName = Path.GetFileName(prefab);
                xml.Add(new XElement("prefab", fileName));
                xml.Add(new XElement("GameObject", fullName));
                SerializeToXml(s, typeof(T), xml);
                XmlRoot.Add(xml);
                changed = true;
            }
            return changed;
        }

#endregion
#region import


        private void Import()
        {
            string title = "Import";
            string path = EditorUtility.OpenFilePanel(title, Application.dataPath, "xml");
            if (path == null)
                return;
            ImportFromFile(title,path);
        }

        private void ImportFromFile(string title,string filename)
        {
            XmlRoot = XDocument.Load(filename).Element("Root");
            IEnumerable<XElement> targetNodes = XmlRoot.Descendants("component");
            EditorUtility.DisplayCancelableProgressBar(title, "wait...", 0);

            int totalCount = 0;
            foreach (var node in targetNodes)
            {
                totalCount++;
            }

            int current = 0;
            foreach (var node in targetNodes)
            {
                string prefab = Path.Combine(AudioUtility.GetXmlProperty(node,"path"), AudioUtility.GetXmlProperty(node, "prefab"));
                prefab = prefab.Replace("/",@"\");
                if (EditorUtility.DisplayCancelableProgressBar(title, prefab, current * 1.0f / totalCount))
                {
                    break;
                }
                ImportPrefab(prefab, node);
                current++;
            }
            EditorUtility.ClearProgressBar();
            AssetDatabase.SaveAssets();
        }

        private void ImportPrefab(string prefab, XElement node)
        {
            string typeName = node.Attribute("type").Value;
            GameObject go = (GameObject)AssetDatabase.LoadAssetAtPath(prefab, typeof(GameObject));
            if (go != null)
            {
                string objName = AudioUtility.GetXmlProperty(node, "GameObject");
                GameObject child = GetGameObject(go, objName);
                if (child != null)
                {
                    Type type = _types[typeName];
                    var component = child.GetComponent(type);
                    if (component == null)
                    {
                        component = child.AddComponent(type);
                    }
                    if (RefreshComponent(child, component, node, type, prefab))
                    {
                        GameObject instance = PrefabUtility.InstantiatePrefab(go) as GameObject;
                        PrefabUtility.ReplacePrefab(instance, go, ReplacePrefabOptions.ConnectToPrefab);
                        UnityEngine.Object.DestroyImmediate(instance);
                    }
                }
            }
        }

        private GameObject GetGameObject(GameObject go, string fullName)
        {
            if (go.name == fullName)
                return go;
            var names = fullName.Split('.');
            return GetGameObject(go, names, 1);
        }

        private GameObject GetGameObject(GameObject go, string[] names, int index)
        {
            if (index > names.Length)
            {
                return null;
            }

            foreach (Transform child in go.transform)
            {
                if (child.gameObject.name == names[index])
                {
                    if (index == names.Length - 1)
                    {
                        return child.gameObject;
                    }
                    return GetGameObject(child.gameObject, names, index + 1);
                }
            }
            return null;
        }

        private bool RefreshComponent(GameObject go, Component component, XElement node, Type type, string prefab)
        {
            ComponentImporter importer = _importers[type.ToString()];
            if(importer != null)
            {
                return importer(go, component, node, prefab);
            }
            return false;
        }

        private bool AnimationSoundImporter(GameObject go, Component component, XElement node, string prefab)
        {
            bool updated = false;
            AnimationSound s = component as AnimationSound;
            if(s != null)
            {
                Animator animator = go.GetComponent<Animator>();
                if (animator == null)
                {
                    return false;
                }

                bool isUpdatePosition = Convert.ToBoolean(AudioUtility.GetXmlProperty(node, "IsUpdatePosition"));                
                if (s.IsUpdatePosition != isUpdatePosition)
                {
                    s.IsUpdatePosition = isUpdatePosition;
                    updated = true;
                }

                var clips = node.Descendants("clip");
                if (clips != null)
                {
                    foreach (var c in clips)
                    {
                        string clipName = AudioUtility.GetXmlProperty(c, "name");
                        foreach (AnimationClip clip in animator.runtimeAnimatorController.animationClips)
                        {
                            if(clip.name == clipName)
                            {
                                AnimationClipModify.ImportEvents(clip,c);
                            }
                        }
                    }
                }
            }
            return updated;
        }

        private TriggerState StringToTriggerState(string state)
        {
            if (state == "Enable")
                return TriggerState.Enable;
            else if (state == "Disable")
                return TriggerState.Disable;
            Debug.LogWarning("Error State:"+state);
            return TriggerState.Enable;
        }

        private bool EffectSoundImporter(GameObject go, Component component, XElement node, string prefab)
        {
            bool updated = false;
            EffectSound s = component as EffectSound;
            if (s != null)
            {
                bool isUpdatePosition = Convert.ToBoolean(AudioUtility.GetXmlProperty(node, "IsUpdatePosition"));
                if (s.IsUpdatePosition != isUpdatePosition)
                {
                    s.IsUpdatePosition = isUpdatePosition;
                    updated = true;
                }
                bool isPlayerControl = Convert.ToBoolean(AudioUtility.GetXmlProperty(node, "PlayerControl"));
                if (s.PlayerControl != isPlayerControl)
                {
                    s.PlayerControl = isPlayerControl;
                    updated = true;
                }
                
                var enables = node.Descendants("AudioStudio.SoundParam");
                List<SoundParam> splist = new List<SoundParam>();
                foreach (var p in enables)
                {
                    SoundParam sp = new SoundParam();
                    bool bstop = Convert.ToBoolean(AudioUtility.GetXmlProperty(p, "stop"));
                    if (sp.stop != bstop)
                    {
                        sp.stop = bstop;
                        updated = true;
                    }
                    float transitionDuration = Convert.ToSingle(AudioUtility.GetXmlProperty(p, "transitionDuration"));
                    if (transitionDuration != sp.transitionDuration)
                    {
                        sp.transitionDuration = transitionDuration;
                        updated = true;
                    }
                    sp.audioEvent = new AudioEventPC();
                    sp.audioEvent.name = Convert.ToString(AudioUtility.GetXmlProperty(p, "audioEvent"));

                    if (ParseEventPC(ref sp.audioEvent, go.name, prefab, "EffectSound"))
                        updated = true;
                    splist.Add(sp);
                }
                s.enables = splist.ToArray();
                var disables = node.Descendants("AudioStudio.AudioEventPC");
                List<AudioEventPC> aelist = new List<AudioEventPC>();
                foreach (var p in disables)
                {
                    AudioEventPC ae = new AudioEventPC();
                    ae.name = p.Value;
                    if (ParseEventPC(ref ae, go.name, prefab, "EffectSound"))
                        updated = true;
                    aelist.Add(ae);
                }
                s.disables = aelist.ToArray();
            }
            return updated;
        }

        private bool SetStateImporter(GameObject go, Component component, XElement node, string prefab)
        {
            bool updated = false;
            SetState s = component as SetState;
            if (s != null)
            {
                XElement States = node.Element("States");
                if (States != null)
                {
                    var param = States.Descendants("AudioStudio.StateParam");

                    List<StateParam> splist = new List<StateParam>();
                    foreach(var p in param)
                    {
                        var trigger = AudioUtility.GetXmlProperty(p, "trigger");
                        StateParam sp = new StateParam();
                        sp.trigger = StringToTriggerState(trigger);
                        sp.state = new State();
                        var ss = AudioUtility.GetXmlProperty(p, "state");
                        var sss = ss.Split('.');
                        if (sss.Length == 2)
                        {
                            sp.state.groupName = sss[0];
                            sp.state.name = sss[1];
                        }
                        else
                        {
                            Debug.LogError("Error State in xml:" + ss);
                        }
                        if (ParseState(ref sp.state, go.name, prefab, "SetState"))
                        {
                            updated = true;
                        }
                        splist.Add(sp);
                    }
                    s.States = splist.ToArray();
                }
            }
            return updated;
        }


        private bool ButtonSoundImporter(GameObject go, Component component, XElement node, string prefab)
        {
            bool updated = false;
            ButtonSound s = component as ButtonSound;
            if (s != null)
            {
                var disables = node.Descendants("AudioStudio.AudioEvent");
                List<AudioEvent> aelist = new List<AudioEvent>();
                foreach (var p in disables)
                {
                    AudioEvent ae = new AudioEvent();
                    ae.name = p.Value;
                    if (ParseEvent(ref ae, go.name, prefab, "ButtonSound"))
                        updated = true;
                    aelist.Add(ae);
                }
                s.audioEvents = aelist.ToArray();

                if (s.mouseinEvent == null)
                    s.mouseinEvent = new AudioEvent();

                s.mouseinEvent.name = AudioUtility.GetXmlProperty(node, "mouseinEvent");
                if (ParseEvent(ref s.mouseinEvent, go.name, prefab, "ButtonSound"))
                    updated = true;
            }
            return updated;
        }

        private bool ToggleSoundImporter(GameObject go, Component component, XElement node, string prefab)
        {
            bool updated = false;
            ToggleSound s = component as ToggleSound;
            if (s != null)
            {
                XElement te = node.Element("toggleaudioEvents");
                if (te != null)
                {
                    var events = te.Descendants("AudioStudio.AudioEvent");
                    List<AudioEvent> aelist = new List<AudioEvent>();
                    foreach (var p in events)
                    {
                        AudioEvent ae = new AudioEvent();
                        ae.name = p.Value;
                        if (ParseEvent(ref ae, go.name, prefab, "ToggleSound"))
                            updated = true;
                        aelist.Add(ae);
                    }
                    s.toggleaudioEvents = aelist.ToArray();
                }
                te = node.Element("untoggleaudioEvents");
                if (te != null)
                {
                    var events = te.Descendants("AudioStudio.AudioEvent");
                    List<AudioEvent> aelist = new List<AudioEvent>();
                    foreach (var p in events)
                    {
                        AudioEvent ae = new AudioEvent();
                        ae.name = p.Value;
                        if (ParseEvent(ref ae, go.name, prefab, "ToggleSound"))
                            updated = true;
                        aelist.Add(ae);
                    }
                    s.untoggleaudioEvents = aelist.ToArray();
                }
            }
            return updated;
        }
        

        private bool MenuSoundImporter(GameObject go, Component component, XElement node, string prefab)
        {
            bool updated = false;
            MenuSound s = component as MenuSound;
            if (s != null)
            {
                XElement opens = node.Element("Opens");
                if (opens != null)
                {
                    var events = opens.Descendants("AudioStudio.AudioEvent");
                    List<AudioEvent> aelist = new List<AudioEvent>();
                    foreach (var p in events)
                    {
                        AudioEvent ae = new AudioEvent();
                        ae.name = p.Value;
                        if (ParseEvent(ref ae, go.name, prefab, "MenuSound"))
                            updated = true;
                        aelist.Add(ae);
                    }
                    s.Opens = aelist.ToArray();
                }

                XElement closes = node.Element("Closes");
                if (closes != null)
                {
                    var events = closes.Descendants("AudioStudio.AudioEvent");
                    List<AudioEvent> aelist = new List<AudioEvent>();
                    foreach (var p in events)
                    {
                        AudioEvent ae = new AudioEvent();
                        ae.name = p.Value;
                        if (ParseEvent(ref ae, go.name, prefab, "MenuSound"))
                            updated = true;
                        aelist.Add(ae);
                    }
                    s.Closes = aelist.ToArray();
                }
            }
            return updated;
        }

        private bool SliderSoundImporter(GameObject go, Component component, XElement node, string prefab)
        {
            bool updated = false;
            SliderSound s = component as SliderSound;
            if (s != null)
            {
                var rtname = node.Element("ValueChangeRTPC").Value;
                s.ValueChangeRTPC.name = rtname;
                updated = ParseRTPC(ref s.ValueChangeRTPC, go.name, prefab, "SliderSound");
            }
            return updated;
        }

        private bool DropdownSoundImporter(GameObject go, Component component, XElement node, string prefab)
        {
            bool updated = false;
            DropdownSound s = component as DropdownSound;
            if (s != null)
            {
                XElement vce = node.Element("ValueChangeEvents");
                if (vce != null)
                {
                    var valuechange = vce.Descendants("AudioStudio.AudioEvent");
                    List<AudioEvent> aelist = new List<AudioEvent>();
                    foreach (var p in valuechange)
                    {
                        AudioEvent ae = new AudioEvent();
                        ae.name = p.Value;
                        if (ParseEvent(ref ae, go.name, prefab, "DropdownSound"))
                            updated = true;
                        aelist.Add(ae);
                    }
                    s.ValueChangeEvents = aelist.ToArray();
                }

                XElement pe = node.Element("PopupEvents");
                if (pe != null)
                {
                    var valuechange = pe.Descendants("AudioStudio.AudioEvent");
                    List<AudioEvent> aelist = new List<AudioEvent>();
                    foreach (var p in valuechange)
                    {
                        AudioEvent ae = new AudioEvent();
                        ae.name = p.Value;
                        if (ParseEvent(ref ae, go.name, prefab, "DropdownSound"))
                            updated = true;
                        aelist.Add(ae);
                    }
                    s.PopupEvents = aelist.ToArray();
                }

                XElement ce = node.Element("CloseEvents");
                if (ce != null)
                {
                    var closeevents = ce.Descendants("AudioStudio.AudioEvent");
                    List<AudioEvent> celist = new List<AudioEvent>();
                    foreach(var p in closeevents)
                    {
                        AudioEvent ae = new AudioEvent();
                        ae.name = p.Value;
                        if (ParseEvent(ref ae, go.name, prefab, "DropdownSound"))
                            updated = true;
                        celist.Add(ae);
                    }
                    s.CloseEvents = celist.ToArray();
                }
            }
            return updated;
        }
        #endregion
    }
}